#!/usr/bin/env python
# -*- coding: utf8 -*-
"""
BlogoSerVer binary daemon
"""

import sys
import hashlib

import libhttpd
import libxmlrpc
import liblm
import libauth

import signal

AUTHDICT    = {} # init authenticated user's dictionary

# def sys method to generate unique user session id
def genUserThreadKey( cliContext ):
  UserThreadKey = hashlib.md5( str( cliContext[0] ) + str( cliContext[2] ) ).hexdigest()
  print 'UserThreadKey: %s' % ( UserThreadKey )
  return UserThreadKey

# HOOK for just connected users
def cliConnected( cliContext ):
  sys.stdout.write( '[%d] Get connected from %s:%d\n' % ( cliContext[0], cliContext[2][0], cliContext[2][1] ) )

  # generate user session key:
  UserThreadKey = genUserThreadKey( cliContext )

def cliDisconnected( cliContext ):
  # generate user session key:
  UserThreadKey = genUserThreadKey( cliContext )

  # FORCE USER LOGOUT
  if AUTHDICT.has_key( UserThreadKey ):
    res = AUTHDICT.pop( UserThreadKey )

  sys.stdout.write( '[%d] User disconnected: %s:%d\n' % ( cliContext[0], cliContext[2][0], cliContext[2][1] ) )

def cliRequest( data, cliContext ):
  """
ok:
(0, (0, []))
 ^          -- libxmlrpc error code
     ^      -- liblm module error code

error in method
>>> cc.methodCall( 'login', [] )
(0, (1, [{'errdesc': 'Wrong argv count', 'errcode': 10}]))
 ^                                                           -- libxmlrpc error code
     ^                                                       -- error was in loaded module
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^     -- returned data

has two state: ( <errcode>, ( <execcode>, [list] ) )
( x, [] ),        where x > 0
( x, ( y, [] ) ), where x == 0;
                        y == 0 -> OK;
                        y > 0  -> FAILED;

  """
  sys.stderr.write( '[%d] Get request from %s:%d:\n%s\n' % ( cliContext[0], cliContext[2][0], cliContext[2][1], data ) )

  # generate user session key:
  UserThreadKey = genUserThreadKey( cliContext )

  try:
    xres = xmlrpc.parseCall( data )
    print 'Parsed arguments:', xres

    res  = [ xmlrpc.convXML2Py( arg ) for arg in xres[2] ]
    print 'Converted arguments:', res

  except:
    sys.stdout.write( '{W} Error while parsing income XML-RPC\n' )
    return xmlrpc.methodResponse_fault( [ xmlrpc.convPy2XML({'errcode':53,'errdesc':'Malformed XML-RPC call'}) ] )


  ################ CATCH SYSTEM METHODS ##########################
  # there are two ways from now... user PUBLIC or PRIVATE call, defined through self.NO_AUTH_REQUIRED
  if xres[1] == 'login':
    print 'CATCH SYSTEM METHODS: login'

    res = auth.login( res ) # res is ( id, name, homepath )

    # IS USER LOGIN?
    if res is None: # failed
      return xmlrpc.methodResponse_fault( [ xmlrpc.convPy2XML( {'errcode':25,'errdesc':'Login failed'} ) ] )

    # chk for res has right format: third part tuple
    elif type( res ) is tuple:
      AUTHDICT[ UserThreadKey ] = res
      #        ^^^^^^                               - user ID
      #                    ^^^^^^                   - user Name
      #                            ^^^^^^           - user Path
      return xmlrpc.methodResponse_ok( [xmlrpc.convPy2XML( {'errcode':0, 'errdesc':'Login success'} )] )

    else: # error format result. Error
      return xmlrpc.methodResponse_fault( [ xmlrpc.convPy2XML( {'errcode':16, 'errdesc':'Inner error. Try to login later'} )] )

  elif xres[1] == 'logout':
    print 'CATCH SYSTEM METHODS: logout'

    if AUTHDICT.has_key( UserThreadKey ):
      print '\tAUTHDICT - OK'
      res = AUTHDICT.pop( UserThreadKey )
      return xmlrpc.methodResponse_ok( [xmlrpc.convPy2XML( {'errcode':0, 'errdesc':'User successfully logout'} ) ] )

    else:
      print '\tAUTHDICT - FAIL'
      return xmlrpc.methodResponse_fault( [xmlrpc.convPy2XML( {'errcode':19, 'errdesc':'User alredy signed out'} )] )

  else:
    print 'CATCH SYSTEM METHODS: other'

    # chk for user already authorized
    if AUTHDICT.has_key( UserThreadKey ):
      print '\tAUTHDICT - OK'
      res  = lm.methodCall( xres[1], AUTHDICT[UserThreadKey], res )
    else:
      print '\tAUTHDICT - FAIL'
      return xmlrpc.methodResponse_fault( [xmlrpc.convPy2XML({'errcode':27,'errdesc':'Authentication required first for this server'})] )

  ############ PARSE MODULED METHOD RESULT SET ###################

  print 'Execution result:', res

  if res[0] == lm.R_OK:                           # OK
    xres = [ xmlrpc.convPy2XML( arg ) for arg in res[1][1] ]

    print '[*] res = ', res[1][1], '; xres = ', xres, ';'

    if res[1][0] == 0:
      return xmlrpc.methodResponse_ok( xres )

    else:
      return xmlrpc.methodResponse_fault( xres )

  elif res[0] == lm.R_BLACKLIST:                  # METHOD BLACKLISTED
    xres = [ xmlrpc.convPy2XML( arg ) for arg in res[1] ]
    return xmlrpc.methodResponse_fault( xres )

  elif res[0] == lm.R_ERROR:                      # ERROR OCCURED IN METHOD
    xres = [ xmlrpc.convPy2XML( arg ) for arg in res[1] ]
    return xmlrpc.methodResponse_fault( xres )

  elif res[0] == lm.R_NOTFOUND:                   # METHOD NOT FOUND
    xres = [ xmlrpc.convPy2XML( arg ) for arg in res[1] ]
    return xmlrpc.methodResponse_fault( xres )

  else:                                           # IN NORMAL CASES, REMOVE THIS
    xres = [ xmlrpc.convPy2XML( arg ) for arg in res[1] ]
    return xmlrpc.methodResponse_fault( xres )

# signal catcher
def sigcatcher( sig ):
  if sig == signal.SIGTERM:
    net.StopServer( )
    sys.stderr.write( '{E} SIGTERM signal catched. Shuting down sockets, threads, etc.' )
    sys.exit( 1 )

  else:
    sys.stderr.write( '{E} Unhandled signal catched\n' )
    sys.exit( 2 )

def showModules( ):
  sys.stderr.write( 'modules:\n\t%s\n' % ( '\n\t'.join( lm.listModules( ) ) ) )

def showMethods( ):
  sys.stderr.write( 'methods:\n\t%s\n' % ( '\n\t'.join( lm.listMethods( ) ) ) )

net    = libhttpd.http_io( )  # implement "HTTP Server" support
xmlrpc = libxmlrpc.XML_RPC( ) # implement "XML-RPC Layer" support
lm     = liblm.BlogoSerV( )   # implement "Loadable Modules" support

# init loadable modules
lm = liblm.BlogoSerV( )

# list modules:
showModules()
# list methods:
showMethods()

# init xml rpc layer
xmlrpc = libxmlrpc.XML_RPC( )

# at last queue, after all
# hooks
net.cb_connect    = cliConnected
net.cb_disconnect = cliDisconnected
net.cb_request    = cliRequest

net.HEADER_MIN_LEN = 100

# set wait timeouts
net.headerWaitTimeOut = 30
net.packetWaitTimeOut = 3

# init auth class
auth = libauth.auth()

# signals
signal.signal( signal.SIGTERM, sigcatcher )

# start server here
net.StartServer( )
net.WaitFor( 25 )

net.StopServer( )
sys.exit( 0 )
